﻿using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

#if NET4_0
namespace System
{
#pragma warning disable 8500
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [DebuggerTypeProxy(typeof(MemoryDebugView<>))]
    [DebuggerDisplay("{ToString(),raw}")]
    public class Memory<T> where T : struct
    {
        /// <summary>
        /// Empty
        /// </summary>
        public static Memory<T> Empty = default(Memory<T>);

        internal readonly T[] _arr;
        internal readonly int _offset;

        private readonly int _length;
        /// <summary>
        /// 数据长度
        /// </summary>
        public int Length
        {
            get
            {
                return this._length;
            }
        }

        /// <summary>
        /// 当前数据是否为空标识
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                return this._length == 0;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        public Span<T> Span
        {
            get
            {
                return new Span<T>(this._arr, this._offset, this._length);
            }
        }

        /// <summary>
        /// 构造初始化
        /// </summary>
        /// <param name="array"></param>
        public Memory(T[] array)
        {
            this._arr = array;
            if (array == null)
            {
                this._offset = 0;
                this._length = 0;
            }
            else
            {
                this._offset = 0;
                this._length = array.Length;
            }
        }

        /// <summary>
        /// 构造初始化
        /// </summary>
        /// <param name="array"></param>
        /// <param name="start"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public Memory(T[] array, int start)
        {
            this._arr = array;
            if (array == null)
            {
                this._offset = 0;
                this._length = 0;
            }
            else
            {
                if (start < 0 || start > array.Length)
                {
                    throw new ArgumentOutOfRangeException(nameof(start));
                }

                this._offset = start;
                this._length = array.Length - start;
            }
        }

        /// <summary>
        /// 构造初始化
        /// </summary>
        /// <param name="array"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public Memory(T[] array, int start, int length)
        {
            this._arr = array;
            if (array == null)
            {
                this._offset = 0;
                this._length = 0;
            }
            else
            {

                if (start < 0 || start > array.Length)
                {
                    throw new ArgumentOutOfRangeException(nameof(start));
                }

                if (start + length > array.Length)
                {
                    throw new ArgumentOutOfRangeException(nameof(length));
                }

                this._offset = start;
                this._length = length;
            }
        }

        /*
        internal Memory(MemoryManager<T> manager, int length)
        {
            if (length < 0)
            {
                System.ThrowHelper.ThrowArgumentOutOfRangeException();
            }

            _arr = manager;
            _offset = int.MinValue;
            _length = length;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal Memory(MemoryManager<T> manager, int start, int length)
        {
            if (length < 0 || start < 0)
            {
                System.ThrowHelper.ThrowArgumentOutOfRangeException();
            }

            _arr = manager;
            _offset = start | int.MinValue;
            _length = length;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal Memory(object obj, int start, int length)
        {
            _arr = obj;
            _offset = start;
            _length = length;
        }
        */

        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        public static implicit operator Memory<T>(T[] array)
        {
            return new Memory<T>(array);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="segment"></param>
        public static implicit operator Memory<T>(ArraySegment<T> segment)
        {
            return new Memory<T>(segment.Array, segment.Offset, segment.Count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="memory"></param>
        public static implicit operator ReadOnlyMemory<T>(Memory<T> memory)
        {
            return new ReadOnlyMemory<T>(memory._arr, memory._offset, memory._length);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Join(",", this._arr, this._offset, this._length);
        }

        /// <summary>
        /// 分片数据
        /// </summary>
        /// <param name="start">起始位置</param>
        /// <returns>分片后的数据</returns>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
        public Memory<T> Slice(int start)
        {
            if (start < 0 || this._offset + start > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(start));
            }

            return new Memory<T>(this._arr, this._offset + start, this._length - start);
        }

        /// <summary>
        /// 分片数据
        /// </summary>
        /// <param name="start">起始位置</param>
        /// <param name="length">数据长度</param>
        /// <returns>分片后的数据</returns>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
        public Memory<T> Slice(int start, int length)
        {
            if (start < 0 || this._offset + start > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(start));
            }

            if (length < 0 || this._offset + start + length > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            return new Memory<T>(_arr, _offset + start, length);
        }

        /// <summary>
        /// 从当前数据拷贝到目标数据
        /// </summary>
        /// <param name="destination">目标数据</param>
        public void CopyTo(Memory<T> destination)
        {
            Array.Copy(this._arr, this._offset, destination._arr, destination._offset, this._length);
        }

        /// <summary>
        /// 尝试从当前数据拷贝到目标数据
        /// </summary>
        /// <param name="destination">目标数据</param>
        /// <returns>拷贝结果</returns>
        public bool TryCopyTo(Memory<T> destination)
        {
            if (destination._length < this._length)
            {
                return false;
            }
            Array.Copy(this._arr, this._offset, destination._arr, destination._offset, this._length);
            return true;
        }

        /// <summary>
        /// 将数组从托管调整为非托管
        /// </summary>
        /// <returns></returns>
        public unsafe MemoryHandle Pin()
        {
            if (this._length < 0)
            {
                return new MemoryHandle(null, new GCHandle());
            }

            GCHandle handle = GCHandle.Alloc(this._arr, GCHandleType.Pinned);
            T* pointer3 = (T*)handle.AddrOfPinnedObject() + this._offset;
            return new MemoryHandle(pointer3, handle);
        }

        /// <summary>
        /// 转换为数组
        /// </summary>
        /// <returns></returns>
        public T[] ToArray()
        {
            var newArr = new T[this._length];
            Array.Copy(this._arr, this._offset, newArr, 0, this._length);
            return newArr;
        }

        /// <summary>
        /// 重写Equals
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Equals(object obj)
        {
            if (obj is ReadOnlyMemory<T> readOnlyMemory)
            {
                return readOnlyMemory.Equals(this);
            }

            if (obj is Memory<T> other)
            {
                return Equals(other);
            }

            return false;
        }

        /// <summary>
        /// 重写Equals
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(Memory<T> other)
        {
            if (this._arr == other._arr && this._offset == other._offset)
            {
                return this._length == other._length;
            }

            return false;
        }

        /// <summary>
        /// 重写GetHashCode
        /// </summary>
        /// <returns></returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override int GetHashCode()
        {
            if (this._arr == null)
            {
                return 0;
            }

            return CombineHashCodes(this._arr.GetHashCode(), this._offset.GetHashCode(), this._length.GetHashCode());
        }

        private static int CombineHashCodes(int left, int right)
        {
            return ((left << 5) + left) ^ right;
        }

        private static int CombineHashCodes(int h1, int h2, int h3)
        {
            return CombineHashCodes(CombineHashCodes(h1, h2), h3);
        }
    }

    internal sealed class MemoryDebugView<T> where T : struct
    {
        private readonly ReadOnlyMemory<T> _memory;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public T[] Items
        {
            get { return this._memory.ToArray(); }
        }

        public MemoryDebugView(Memory<T> memory)
        {
            this._memory = memory;
        }

        public MemoryDebugView(ReadOnlyMemory<T> memory)
        {
            this._memory = memory;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    unsafe public class ReadOnlyMemory<T> where T : struct
    {
        /// <summary>
        /// Gets an empty System.ReadOnlyMemory
        /// </summary>
        public static ReadOnlyMemory<T> Empty = default(ReadOnlyMemory<T>);


        private readonly T[] _arr;
        private readonly int _offset;
        private readonly int _length;
        /// <summary>
        /// 数据长度
        /// </summary>
        public int Length
        {
            get
            {
                return this._length;
            }
        }

        /// <summary>
        /// 当前数据是否为空标识
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                return this._length == 0;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ReadOnlySpan<T> Span
        {
            get
            {
                return new ReadOnlySpan<T>(this._arr, this._offset, this._length);
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        public ReadOnlyMemory(T[] array)
        {
            this._arr = array;
            if (array == null)
            {
                this._offset = 0;
                this._length = 0;
            }
            else
            {
                this._offset = 0;
                this._length = array.Length;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        public ReadOnlyMemory(T[] array, int start, int length)
        {
            this._arr = array;
            if (array == null)
            {
                this._offset = 0;
                this._length = 0;
            }
            else
            {
                if (start < 0 || start > array.Length)
                {
                    throw new ArgumentOutOfRangeException(nameof(start));
                }

                this._offset = start;
                this._length = array.Length - start;
            }
        }






        /// <summary>
        /// Copies the contents of the read-only memory region into a destination memory region.
        /// </summary>
        /// <param name="destination"></param>
        public void CopyTo(Memory<T> destination)
        {
            Array.Copy(this._arr, this._offset, destination._arr, destination._offset, this._length);
        }

        /// <summary>
        /// Tries to copy the contents of the readonly-only memory into the destination and returns a value that indicates whether the copy operation succeeded.
        /// </summary>
        /// <param name="destination"></param>
        /// <returns></returns>
        public bool TryCopyTo(Memory<T> destination)
        {
            if (destination.Length < this._length)
            {
                return false;
            }
            Array.Copy(this._arr, this._offset, destination._arr, destination._offset, this._length);
            return true;
        }


        /// <summary>
        /// Determines whether the current instance and a specified System.ReadOnlyMemory`1 objects are equal.
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(ReadOnlyMemory<T> other)
        {
            if (this._arr == other._arr && this._offset == other._offset)
            {
                return this._length == other._length;
            }

            return false;
        }

        /// <summary>
        /// 重写Equals
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Equals(object obj)
        {
            if (obj is ReadOnlyMemory<T> readOnlyMemory)
            {
                return readOnlyMemory.Equals(this);
            }

            if (obj is Memory<T> other)
            {
                return Equals(other);
            }

            return false;
        }

        /// <summary>
        /// 重写GetHashCode
        /// </summary>
        /// <returns></returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override int GetHashCode()
        {
            if (this._arr == null)
            {
                return 0;
            }

            return CombineHashCodes(this._arr.GetHashCode(), this._offset.GetHashCode(), this._length.GetHashCode());
        }

        private static int CombineHashCodes(int left, int right)
        {
            return ((left << 5) + left) ^ right;
        }

        private static int CombineHashCodes(int h1, int h2, int h3)
        {
            return CombineHashCodes(CombineHashCodes(h1, h2), h3);
        }


        /// <summary>
        /// Creates a handle for the memory.
        /// </summary>
        /// <returns>A handle for the memory.</returns>
        public MemoryHandle Pin()
        {
            if (this._length < 0)
            {
                return new MemoryHandle(null, new GCHandle());
            }

            GCHandle handle = GCHandle.Alloc(this._arr, GCHandleType.Pinned);
            T* pointer3 = (T*)handle.AddrOfPinnedObject() + this._offset;
            return new MemoryHandle(pointer3, handle);
        }

        /// <summary>
        /// Forms a slice out of the given memory region starting at start position for length elements.
        /// </summary>
        /// <param name="start">The index at which to begin this slice.</param>
        /// <returns>A read-only memory region representing the desired slice.</returns>
        public ReadOnlyMemory<T> Slice(int start)
        {
            if (start < 0 || this._offset + start > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(start));
            }

            return new ReadOnlyMemory<T>(this._arr, this._offset + start, this._length - start);
        }

        /// <summary>
        /// Forms a slice out of the given memory region starting at start position for length elements.
        /// </summary>
        /// <param name="start">The index at which to begin this slice.</param>
        /// <param name="length">The desired length for the slice</param>
        /// <returns>A read-only memory region representing the desired slice.</returns>
        public ReadOnlyMemory<T> Slice(int start, int length)
        {
            if (start < 0 || this._offset + start > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(start));
            }

            if (length < 0 || this._offset + start + length > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            return new ReadOnlyMemory<T>(_arr, _offset + start, length);
        }

        /// <summary>
        /// Copies the contents from the memory region into a new array.
        /// </summary>
        /// <returns></returns>
        public T[] ToArray()
        {
            var newArr = new T[this._length];
            Array.Copy(this._arr, this._offset, newArr, 0, this._length);
            return newArr;
        }

        /// <summary>
        /// Returns the string representation of this read-only memory instance.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Join(",", this._arr, this._offset, this._length);
        }


        /// <summary>
        /// Defines an implicit conversion of a System.ArraySegment`1 to a System.ReadOnlyMemory`1.
        /// </summary>
        /// <param name="segment"></param>
        public static implicit operator ReadOnlyMemory<T>(ArraySegment<T> segment)
        {
            return new ReadOnlyMemory<T>(segment.Array, segment.Offset, segment.Count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>

        public static implicit operator ReadOnlyMemory<T>(T[] array)
        {
            return new ReadOnlyMemory<T>(array);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    unsafe public struct MemoryHandle : IDisposable
    {
        /// <summary>
        /// 数据指针
        /// </summary>
        public readonly void* Pointer;

        private readonly GCHandle _handle;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ptr"></param>
        /// <param name="handle"></param>
        public MemoryHandle(void* ptr, GCHandle handle)
        {
            this.Pointer = ptr;
            this._handle = handle;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            this._handle.Free();
        }
    }
}
#endif